#include <func.h>

int main()
{
    //创建服务器的监听套接字
    int listenfd = socket(AF_INET, SOCK_STREAM, 0);
    ERROR_CHECK(listenfd, -1, "socket");

    //设置套接字的网络地址可以重用
    int on = 1;
    int ret = setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
    ERROR_CHECK(ret, -1, "setsockopt");

    struct sockaddr_in serveraddr;
    memset(&serveraddr, 0, sizeof(serveraddr));
    //指定使用的是IPv4的地址类型 AF_INET
    serveraddr.sin_family = AF_INET;
    serveraddr.sin_port = htons(8080);
    serveraddr.sin_addr.s_addr = inet_addr("192.168.30.129");

    //以人类可阅读的方式打印网络地址
    printf("%s:%d\n", 
           inet_ntoa(serveraddr.sin_addr),
           ntohs(serveraddr.sin_port));

    //绑定服务器的网络地址
    ret = bind(listenfd, (const struct sockaddr*)&serveraddr, 
                   sizeof(serveraddr));
    ERROR_CHECK(ret, -1, "bind");

    //监听客户端的到来
    ret = listen(listenfd, 10);

    fd_set rdset;
    FD_ZERO(&rdset);
    char buff[100] = {0};//自定义的用户态缓冲区
    //conns保存的是所有的已经建立好连接的peerfd
    int conns[1024] = {0};
    int maxfd = listenfd;//当前监听的最大文件描述符的值

    //事件循环
    while(1) {
        //对于每一次循环，都会先清空rdset
        FD_ZERO(&rdset);
        //监听的服务器套接字
        FD_SET(listenfd, &rdset);
        //监听已经建立好的连接peerfd
        for(int i = 0; i < 1024; ++i) {
            if(conns[i] != 0) {
                FD_SET(conns[i], &rdset);
            }
        }
        //当select函数返回时，rdset会被修改的
        ret = select(maxfd + 1, &rdset, NULL, NULL, NULL);//阻塞式函数
        printf("select ret: %d\n", ret);

        //对于新连接的处理
        if(FD_ISSET(listenfd, &rdset)) {
            //处理一个客户端的请求
            struct sockaddr_in clientaddr;
            socklen_t len = sizeof(clientaddr);
            int peerfd = accept(listenfd, (struct sockaddr*)&clientaddr, &len);
            printf("client %s:%d has connected.\n",
                   inet_ntoa(clientaddr.sin_addr),
                   ntohs(clientaddr.sin_port));
            //将peerfd存储到conns中
            for(int i = 0; i < 1024; ++i) {
                if(conns[i] == 0) {
                    conns[i] = peerfd;
                    break;
                }
            }
            //更新maxfd的值
            if(maxfd < peerfd) {
                maxfd = peerfd;
            }
        }

        //从客户端接收数据, 要遍历conns,判断其中的连接
        //是否在rdset中, 如果在就直接采用原有方案
        for(int i = 0; i < 1024; ++i) {
            if(conns[i] != 0 && FD_ISSET(conns[i], &rdset)) {
                memset(buff, 0, sizeof(buff));
                //当对端断开之后，使用select监听peerfd的读事件，
                //会一直触发(水平触发)
                ret = recv(conns[i], buff, sizeof(buff), 0);
                if(ret == 0) {
                    //recv的返回值为0，说明连接已经断开了
                    printf("byebye.\n");
                    close(conns[i]);
                    conns[i] = 0;
                    continue;
                }
                printf("ret: %d, recv: %s\n", ret, buff);
                //将conns[i]所接收的数据再转发给所有已经进入聊天室的用户
                for(int j = 0; j < 1024; ++j) {
                    if(i != j && conns[j]!=0) 
                        send(conns[j], buff, ret, 0);
                }   
            }
        }
    }

    close(listenfd);
    return 0;
}
